﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace CodeReborn.Classes
{
    static public class MemoryReader
    {

        /// <summary>
        /// 
        /// </summary>
        /// <param name="process"></param>
        /// <param name="startAddress"></param>
        /// <param name="buffer"></param>
        /// <param name="offsets"></param>
        /// <returns></returns>
        public static bool ReadMemory(Process process, ProcessModule processModule, IntPtr startAddress, byte[] buffer, short[] offsets = null)
        {
            if (process == null || processModule == null || startAddress == null)
                return false;
            //I don't know what this does exactly
            var lpOutStorage = IntPtr.Zero;
            var currentAddress = new IntPtr(startAddress.ToInt32());

            //Iterate over offsets to traverse the chain of pointers and find the final address.
            if (offsets != null && offsets.Length > 0)
            {
                //This will store temporary addresses
                var addressValue = new byte[4];
                int i = 0;
                do
                {
                    if (!NativeMethods.ReadProcessMemory(process.Handle, currentAddress, addressValue, (uint)addressValue.Length, ref lpOutStorage))
                        return false; //failed to read memory. Exit with false.
                    currentAddress = (IntPtr)BitConverter.ToInt32(addressValue, 0) + offsets[i];
                    i++;
                } while (i < offsets.Length);
            }

            //Read the value.
            return NativeMethods.ReadProcessMemory(process.Handle, currentAddress, buffer, (uint)buffer.Length, ref lpOutStorage);
        }

    }

    /// <summary>
    /// private NativeMethods Import Definitions
    /// 
    /// </summary>
    internal static class NativeMethods
    {
        /// <summary>
        /// kernel32.ReadProcessMemory Import
        /// </summary>
        /// <param name="hProcess"></param>
        /// <param name="lpBaseAddress"></param>
        /// <param name="lpBuffer"></param>
        /// <param name="nSize"></param>
        /// <param name="lpNumberOfBytesRead"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern bool ReadProcessMemory(
            IntPtr hProcess,
            IntPtr lpBaseAddress,
            [In, Out] byte[] lpBuffer,
            UInt32 nSize,
            ref IntPtr lpNumberOfBytesRead
            );

        /// <summary>
        /// kernel32.WriteProcessMemory Import
        /// </summary>
        /// <param name="hProcess"></param>
        /// <param name="lpBaseAddress"></param>
        /// <param name="lpBuffer"></param>
        /// <param name="nSize"></param>
        /// <param name="lpNumberOfBytesWritten"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern bool WriteProcessMemory(
            IntPtr hProcess,
            IntPtr lpBaseAddress,
            [In, Out] byte[] lpBuffer,
            UInt32 nSize,
            ref IntPtr lpNumberOfBytesWritten
            );
    }
}
